#include <string.h>
#include <stdarg.h>
#include "li_collections.h"
#include "hashlib.h"
#include "li_pipe.h"
#include "li_list.h"
#include "li_vm.h"
#include "li_vars.h"
#include "cre.h"
#include "li_basic.h"
#include "RPE.h"
#include "li_compile.h"

#define __exitEr          \
	{                     \
		return -__LINE__; \
	}
#define __ec -__LINE__

// RE _loop_head = NULL, _if_head = NULL, _var_assign_pat = NULL, _fun_call_pat;
RE _re_first_token = NULL; //^\\s*([^\\W\\d]\\w*)\\s*
RE _re_empty_pat = NULL;   //^\\s*$
RE _re_for_pat = NULL;	   //^(\\w+)\\s*=([^:]+)\\s*:\\s*(?:([^:]+)\\s*:\\s*)?([^:]+)\\s*$
// (r['\"]|[\\n;#'\"])
// ([\\n;#])
// ([\"']|r['\"])
RE _re_delimiter_general = NULL, _re_delimiter_eos, _re_delimiter_quote;
// ^\s*(\w+)\s*(=,)?
RE _re_var_pat = NULL;
void _compile_re_setup()
{
	if (_re_first_token)
	{
		return;
	}
	_re_first_token = fre_compile("^\\s*([^\\W\\d]\\w*)\\s*");
	_re_for_pat = fre_compile("^(\\w+)\\s*=([^:]+)\\s*:\\s*(?:([^:]+)\\s*:\\s*)?([^:]+)\\s*$");
	_re_empty_pat = fre_compile("^\\s*$");
	_re_delimiter_general = fre_compile("(r['\"]|[\\n;#'\"])");
	_re_delimiter_eos = fre_compile("([\\n;#])");
	_re_delimiter_quote = fre_compile("([\"']|r['\"])");
	_re_var_pat = fre_compile("^\\s*(\\w+)\\s*([=,]?)"); // ^(\w+)\s*=\s*.*
}

#pragma region MemorySetup
#define GetStrHeap(args) (list_content(args, Any)[0])

Li_List _new_args_array()
{
	Li_List args = new_li(Any);
	Li_List strs = new_li(char);
	li_add(args, strs);
	return args;
}
void _delete_args_array(Li_List args)
{
	Li_List sts = (Li_List)GetStrHeap(args);
	delete_list(sts);
	delete_list(args);
}
str _get_arg_str(Li_List v, int index)
{
	return list_content(v, char) + index;
}

int32_t _push_arg_str(Li_List v, str s, int n)
{
	n++;
	char *cs = list_content(v, char);
	int rtc_index = v->count;
	cs += rtc_index;
	if (v->count + n + 1 < v->cap)
	{
		memcpy(cs, s, n);
		v->count += n;
		list_content(v, char)[v->count - 1] = 0;
		return rtc_index;
	}
	int next_cap = v->cap;
	do
	{
		next_cap = next_cap > 1024 ? next_cap + n : next_cap * 2;
	} while (next_cap <= n);
	if (list_recap(v, next_cap) < 0)
		return -1;
	cs = list_content(v, char);
	cs += rtc_index;
	memcpy(cs, s, n);
	v->count += n;
	list_content(v, char)[v->count - 1] = 0;
	return rtc_index;
}
int _push_ins(Li_List ins, Li_List args, uint16_t opeCode, int argn, ...)
{
	int args_at = args->count;
	if (!argn)
	{
		Li_Ins_t oper = {opeCode, 0};
		li_add(ins, oper);
		return args_at;
	}
	Li_Ins_t oper = {opeCode, args_at};
	li_add(ins, oper);
	va_list valist;
	va_start(valist, argn);
	for (int i = 0; i < argn; i++)
	{
		int32_t d = va_arg(valist, int32_t);
		li_add(args, d);
	}
	va_end(valist);
	return args_at;
}

int _push_ins2(Li_List ins, Li_List args, uint16_t opeCode, int argn)
{
	int args_at = args->count;
	if (!argn)
	{
		Li_Ins_t oper = {opeCode, 0};
		li_add(ins, oper);
		return 0;
	}
	Li_Ins_t oper = {opeCode, args_at};
	li_add(ins, oper);
	return 0;
}
int _push_args(Li_List args, int argn, ...)
{
	va_list valist;
	va_start(valist, argn);
	for (int i = 0; i < argn; i++)
	{
		int32_t d = va_arg(valist, int32_t);
		li_add(args, d);
	}
	va_end(valist);
	return 0;
}
// return index storing data
int _push_args2(Li_List args, Any data)
{
	return li_add(args, data);
}
// push a ovject to args segment, return index storing data
int _push_args3(Li_List args, Any _data, int size)
{
	int8_t *data = (int8_t *)_data;
	int top = args->count;
	for (int i = 0; i < size; i += sizeof(Any))
	{
		list_add(args, data + i);
	}
	return top;
}
// dummy write
int _push_args4(Li_List args, int size)
{
	int top = args->count;
	for (int i = 0; i < size; i += sizeof(Any))
	{
		if (args->count >= args->cap)
		{
			if (list_recap(args, args->cap + 4) < 0)
			{
				if (list_recap(args, args->cap + 1) < 0)
					return -1;
			}
		}
		args->count++;
	}
	return top;
}

typedef enum
{
	_Ext_end_for_goto,
	_Ext_end_callback,
	_Ext_end_while_goto,
	_Ext_end_loop_goto,
	_Ext_while_gap,
	_Ext_ifc
} _ExtHC;

int _push_ext(Li_List ext, Object obj)
{
	return list_add(ext, obj);
}
int _push_ext2(Li_List ext, HASH hs, Any data)
{
	Object_t obj = {.hashCode = hs, .data = data};
	return list_add(ext, &obj);
}
int _pop_ext(Li_List ext, HASH hs, Object out_res)
{
	int ind = searchObj3_t(ext, hs);
	if (ind < 0)
	{
		return -1;
	}
	Object res = list_content(ext, Object_t) + ind;
	*out_res = *res;
	ext->count--;
	if (ind == ext->count)
		return 0;
	// 0 1 2 3 4
	int remain_count = ext->count - ind;
	out_res = res + 1;
	memmove(res, out_res, remain_count * ext->type_size);
	return 0;
}

/*
kw1: last_goto
kw2: end_goto
kw3: List<int*>
end-cb: function <>

*/
#pragma endregion

#pragma region compile xxx
typedef int (*CompileProtocol)(const str rx, int len, Li_List ins, Li_List args, void *ptr_ext);
// @NotYet
int _compile_cmd(str rx, int len, Li_List cmds_lib, Li_List ins, Li_List args)
{
	Li_List tokens = str_cmd_split(rx);
	if (!tokens->count)
		goto exit;
	// CMD [cmd_address] [argn] [argv: str]...
	str *ts = list_content(tokens, str);
	HASH hs = getHashCodeS(*ts);
	Li_List strheap = GetStrHeap(args);
	int ind = searchObj2(cmds_lib, *ts, hs);
	if (ind < 0)
		goto exit;
	Cmd cmd_ptr = list_content(cmds_lib, Cmd_t) + ind;
	if (tokens->count == 1)
	{
		_push_ins(ins, args, _cmd, 0);
		goto exit;
	}
	_push_ins2(ins, args, _cmd, 3);
	_push_args(args, 2, cmd_ptr, tokens->count);
	for (int i = 1; i < (int)(tokens->count); i++)
	{
		int32_t s = _push_arg_str(strheap, ts[i], strlen(ts[i]));
		if (s < 0)
			goto exit;
		_push_args2(args, s);
	}
exit:
	delete_list(tokens);
	return 0;
}

int _compile_cal(str rx, int len, Li_List ins, Li_List args)
{
	Li_List _block = RPE_compile2(rx, len, _push_arg_str, GetStrHeap(args));
	if (!_block)
	{
		return __ec;
	}
	int n = _block->count;
	Li_List cbs = RPE_compress(_block);
	delete_list(_block);
	if (!cbs)
	{
		return __ec;
	}
	// CAL n, "epx"
	_push_ins2(ins, args, _cal, 2);
	_push_args2(args, cbs->cap);
	int ind = _push_args3(args, cbs->content, cbs->type_size * cbs->count);
	delete_list(cbs);
	return ind;
}
#pragma region COMPILE_IF
/*
if A
...
elif B
...
elif C
...
else
...
end
-------------------------
cal "A"
ifn [last_goto]
...
goto [end_goto]
cal "B"
ifn [last_goto]
...
goto [end_goto]
cal "C"
ifn [last_goto]
...
goto [end_goto]
...
-------------------
if A
....
end
------------------
cal a
ifn [last_goto]
....

*/

static int compile_if_end(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	Object_t obj;
	Li_List end_goto = NULL;
	// set the [last_goto] if any
	int ok = _pop_ext((Li_List)ptr_ext, _Ext_ifc, &obj);
	int combine = 0;
	if (ok < 0)
	{
		return __ec;
	}
	combine = obj.data;
	if (combine)
	{
		list_content(args, Any)[combine & 0xffff] = ins->count - (combine >> 16);
	}
	// set [end_goto]
	end_goto = (Li_List)(obj.name);
	if (!end_goto)
		return 0;
	// set every [end_goto]
	int32_t *args_lib = list_content(args, int32_t);
	int now_pc = ins->count;
	int n = end_goto->count;
	int32_t *combines = list_content(end_goto, int32_t);
	for (int i = 0; i < n; i++)
	{
		int comb = combines[i];
		int ind = comb & 0xffff;
		int lpc = comb >> 16;
		args_lib[ind] = now_pc - lpc;
	}
	delete_list(end_goto);
	return 0;
}

int compile_if(str rx, int len, Li_List ins, Li_List args, void *exb)
{
	// Li_List bs = RPE_compile(rx, len);
	// if (!bs)
	// {
	// 	return __ec;
	// }
	// // cal n,"A"
	// _push_ins2(ins, args, _cal, 2);
	// _push_args2(args, (bs->count));
	// _push_args3(args, bs->content, bs->count * bs->type_size);
	// delete_list(bs);
	if (_compile_cal(rx, len, ins, args) < 0)
		return __ec;
	// if-not [last_goto]
	int ind = _push_ins(ins, args, _if_not, 1, 0);
	int combine = ind | (ins->count << 16);
	Object_t obj = {.hashCode = _Ext_ifc, .data = combine, .name = NULL};
	_push_ext(exb, &obj);
	_push_ext2(exb, _Ext_end_callback, compile_if_end);
	return 0;
}

static int compile_elif(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	// goto [end_goto]
	// cal b
	// ifn [last_goto]
	Li_List bs = RPE_compile2(rx, len, _push_arg_str, GetStrHeap(args));
	if (!bs)
	{
		return __ec;
	}
	// goto [end_goto]
	int ind = _push_ins(ins, args, _goto, 1, 0);
	Object_t obj;
	int combine;
	Li_List end_goto = NULL;
	int ok = _pop_ext((Li_List)ptr_ext, _Ext_ifc, &obj);
	if (ok < 0)
		return __ec;
	if (!obj.name)
	{
		end_goto = new_li(int);
		obj.name = (char *)(Any)end_goto;
		if ((ind | (ins->count)) & 0xffff0000)
		{
			goto er;
		}
		int combine = ind | ((ins->count) << 16);
		li_add(end_goto, combine);
	}
	else
	{
		end_goto = (Li_List)(obj.data);
		if ((ind | (ins->count)) & 0xffff0000)
			goto er;
		combine = ind | ((ins->count) << 16);
		li_add(end_goto, combine);
	}
	combine = obj.data;
	list_content(args, Any)[combine & 0xffff] = ins->count - (combine >> 16);
	// cal n,"B"
	Li_List cbs = RPE_compress(bs);
	if (!cbs)
		return __ec;

	_push_ins2(ins, args, _cal, 2);
	_push_args2(args, (bs->count));
	_push_args3(args, cbs->content, cbs->count * cbs->type_size);
	delete_list(bs);
	delete_list(cbs);

	// if-not [last_goto]
	ind = _push_ins(ins, args, _if_not, 1, 0);
	if ((ind | (ins->count)) & 0xffff0000)
		goto er;
	combine = ind & ((ins->count) << 16);
	obj.data = combine;
	_push_ext((Li_List)ptr_ext, &obj);
	return 0;
er:
	if (end_goto)
		delete_list(end_goto);
	return __ec;
}
static int compile_else(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	// goto [end_goto]
	int ind = _push_ins(ins, args, _goto, 1, 0);
	Object_t obj;
	Li_List end_goto = NULL;
	int combine = 0;
	int ok = _pop_ext((Li_List)ptr_ext, _Ext_ifc, &obj);
	if (ok < 0)
		goto er;
	end_goto = (Li_List)(obj.name);
	if (!end_goto)
	{
		end_goto = new_li(int);
		obj.name = (char *)(end_goto);
		if ((ind | (ins->count)) & 0xffff0000)
			goto er;
		combine = ind | ((ins->count) << 16);
		li_add(end_goto, combine);
	}
	else
	{
		end_goto = (Li_List)(obj.data);
		if ((ind | (ins->count)) & 0xffff0000)
			return __ec;
		int combine = ind | ((ins->count) << 16);
		li_add(end_goto, combine);
	}
	combine = obj.data;
	list_content(args, Any)[combine & 0xffff] = ins->count - (combine >> 16);
	obj.data = NULL;
	_push_ext((Li_List)ptr_ext, &obj);
	return 0;
er:
	if (end_goto)
		delete_list(end_goto);
	return __ec;
}
#pragma endregion

#pragma region COMPILE loop - for - while
static int _compile_loop_end(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	Li_List ext = (Li_List)ptr_ext;
	Object_t obj;
	if (_pop_ext(ext, _Ext_end_loop_goto, &obj) < 0)
		return -__LINE__;
	_push_ins(ins, args, _continue, 0);
	int now = ins->count;
	int last = (int32_t)(obj.data);
	int32_t store_index = (int32_t)(void *)(obj.name);
	list_content(args, int32_t)[store_index] = now - last;
	return 0;
}

static int compile_loop(str rx, int len, Li_List ins, Li_List args, void *ext)
{
	/* loop a+1
	------------------
	cal n,"a+1" ->(res)
	regn +0 [end-goto] (loop_count:Val_t)
	loop
	...
	continue
	------------------
	*/
	// Li_List bs = RPE_compile(rx, len);
	// if (!bs)
	// 	return __ec;
	// // cal n,"a+1"
	// _push_ins2(ins, args, _cal, 2);
	// _push_args2(args, (bs->count));
	// _push_args3(args, bs->content, bs->count * bs->type_size);
	// delete_list(bs);
	_compile_cal(rx, len, ins, args);
	// regn +0 [end-goto] (n:Val_t)
	int ind = _push_ins2(ins, args, _regn, 2);
	_push_args2(args, 0i32);
	ind = _push_args4(args, sizeof(int32_t));
	Object_t end_goto_offset = {.hashCode = _Ext_end_loop_goto};
	end_goto_offset.data = (Any)(ins->count);
	end_goto_offset.name = (str)(void *)ind;
	_push_ext(ext, &end_goto_offset);
	end_goto_offset.hashCode = _Ext_end_callback;
	end_goto_offset.data = _compile_loop_end;
	_push_ext(ext, &end_goto_offset);
	// loop
	_push_ins2(ins, args, _loop, 0);
	return 0;
}

/* for i=a:b:c
----------------------------------------------
cal		n, "a" ->(res:Val_t)
set		i:str, hs(i), (res:Val_t)
index	i:str, hs(i), ->(ref:Val)
cal		n, "b" ->(res)
cal		n, "c" ->(res)
reg		+2 [*end_goto_offset*]
frt
goto 	+1
for 	^[i:Val|X1], ^[b:Val_t|X2], ^[c:Val_t|X3]
....
continue
cs		4+8+8
----------------------------------------------*/

static int _compile_for_end(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	Li_List ext = (Li_List)ptr_ext;
	Object_t obj;
	if (_pop_ext(ext, _Ext_end_for_goto, &obj) < 0)
		return __ec;
	_push_ins(ins, args, _continue, 0);
	int now = ins->count;
	int last = (int32_t)(obj.data);
	int32_t store_index = (int32_t)(obj.name);
	list_content(args, int32_t)[store_index] = now - last;
	// 	cs		4+8+8
	_push_ins(ins, args, _cs, 1, (sizeof(Val) + sizeof(Val_t) * 2));
	return 0;
}
static int compile_for(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	// 0-1, 2-3(i), 4-5(a), 6-7(b), 8-9(c)
	int vec[3 * 5];
	Li_List ext = (Li_List)ptr_ext;
	Object_t end_goto_offset = {.hashCode = _Ext_end_for_goto};
	int *ok = fre_search2(_re_for_pat, rx, len, vec, sizeof(vec) / sizeof(*vec));
	if (!ok)
		return __ec; // @error:syntax error
	str s = rx + vec[2];
	int lc_len = _RE_GetGroupLen(vec, 1);
	if (isD(*s))
	{
		return __ec; //@error:syntax error
	}

	Li_List _block = NULL;
	int32_t _i = NULL;
	int32_t X1 = 0, X2 = 0, X3 = 0;
	int ind = 0;
	HASH hs = getHashCode(s, lc_len);
	_i = _push_arg_str(GetStrHeap(args), s, lc_len);
	if (_i < 0)
		__exitEr;
	s = rx + vec[4]; // a
	lc_len = _RE_GetGroupLen(vec, 2);
	// _block = RPE_compile(s, lc_len);
	// if (!_block)
	// {
	// 	goto er;
	// }
	// // CAL n, "epx"
	// _push_ins2(ins, args, _cal, 2);
	// _push_args2(args, (_block->count));
	// _push_args3(args, _block->content, _block->type_size * _block->count);
	// delete_list(_block);
	// _block = NULL;
	if (_compile_cal(s, lc_len, ins, args) < 0)
	{
		goto er;
	} // set		i:str, hs(i), (res:Val_t)
	_push_ins(ins, args, _set, 2, _i, hs);
	// index	i:str, hs(i), ->(ref:Val)
	ind = _push_ins(ins, args, _index, 2, _i, hs);
	// X1 = (int32_t*)list_index(args, ind);
	// X1 = ind + 2;

	// CAL		n, "b" ->(res)
	if (vec[6] != vec[7])
	{
		s = rx + vec[6]; // b
		lc_len = _RE_GetGroupLen(vec, 3);
	}
	else
	{
		s = "1"; // b
		lc_len = 1;
	}
	// _block = RPE_compile(s, lc_len);
	// if (!_block)
	// {
	// 	goto er;
	// }
	// _push_ins2(ins, args, _cal, 2);
	// _push_args2(args, (_block->count));
	// _push_args3(args, _block->content, _block->type_size * _block->count);
	// delete_list(_block);
	// _block = NULL;
	if (_compile_cal(s, lc_len, ins, args) < 0)
	{
		goto er;
	}
	// cal		n, "c" ->(res)
	s = rx + vec[8]; // c
	lc_len = _RE_GetGroupLen(vec, 4);
	// _block = RPE_compile(s, lc_len);
	// if (!_block)
	// {
	// 	goto er;
	// }
	// _push_ins2(ins, args, _cal, 2);
	// _push_args2(args, (_block->count));
	// _push_args3(args, _block->content, _block->type_size * _block->count);
	// delete_list(_block);
	// _block = NULL;
	if (_compile_cal(s, lc_len, ins, args) < 0)
	{
		goto er;
	}

	// reg		+2 [*end_goto_offset*]
	ind = _push_ins(ins, args, _reg, 2, 2i32, ind) + 1;
	end_goto_offset.data = (Any)(ins->count);
	end_goto_offset.name = (str)(void *)ind; // list_index(args, ind);
	_push_ext(ext, &end_goto_offset);
	end_goto_offset.hashCode = _Ext_end_callback;
	end_goto_offset.data = _compile_for_end;
	_push_ext(ext, &end_goto_offset);
	// 	frt
	_push_ins(ins, args, _frt, 0);
	// goto 	+1
	_push_ins(ins, args, _goto, 1, 1i32);
	// for ^(i:Val) ^(b:Val_t) ^(c:Val_t)
	_push_ins2(ins, args, _for, 0);

	if (_block)
	{
		delete_list(_block);
	}
	return 0;
er:
	if (_block)
	{
		delete_list(_block);
	}
	return __ec;
}
static int _compile_while_end(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	Li_List ext = (Li_List)ptr_ext;
	Object_t obj;
	if (_pop_ext(ext, _Ext_end_while_goto, &obj) < 0)
		return __ec;
	_push_ins(ins, args, _continue, 0);
	int now = ins->count;
	int last = (int32_t)(obj.data);
	int32_t store_index = (int32_t)(obj.name);
	now -= last;
	list_content(args, int32_t)[store_index] = now;
	if (_pop_ext(ext, _Ext_while_gap, &obj) < 0)
		return __ec;
	last = (int32_t)(obj.data);
	list_content(args, int32_t)[last] = now - 2;
	return 0;
}
static int compile_while(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	int ind = 0;
	/* while A
	------------
	reg +0 [*end-goto*]
	cal n a
	if-not [*end-goto*] (res)
	....
	continue
	------------
	*/
	Li_List bs = RPE_compile2(rx, len, _push_arg_str, GetStrHeap(args));
	if (!bs)
	{
		return __ec;
	}
	ind = _push_ins(ins, args, _reg, 2, 0, ind) + 1;

	Object_t end_goto_offset = {.hashCode = _Ext_end_while_goto};
	end_goto_offset.data = (Any)(ins->count);
	end_goto_offset.name = (str)(void *)ind;
	_push_ext((Li_List)ptr_ext, &end_goto_offset);
	end_goto_offset.hashCode = _Ext_end_callback;
	end_goto_offset.data = _compile_while_end;
	_push_ext((Li_List)ptr_ext, &end_goto_offset);

	int n = bs->count;
	Li_List cbs = RPE_compress(bs);
	delete_list(bs);
	if (!cbs)
	{
		return __ec;
	}
	// CAL n, "epx"
	_push_ins2(ins, args, _cal, 2);
	_push_args2(args, n);
	ind = _push_args3(args, cbs->content, cbs->type_size * cbs->count);
	delete_list(cbs);

	// _push_ins(ins, args, _cal, 1, bs->count);
	// _push_args3(args, bs->content, bs->type_size * bs->count);
	// delete_list(bs);
	_push_ins2(ins, args, _if_not, 1);
	ind = _push_args4(args, sizeof(int32_t));
	end_goto_offset.hashCode = _Ext_while_gap;
	end_goto_offset.data = (Any)ind;
	_push_ext((Li_List)ptr_ext, &end_goto_offset);
	return 0;
}
static int compile_continue(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	if (*rx)
	{
		int vec[3];
		int *ok = fre_search2(_re_empty_pat, rx, len, vec, sizeof(vec) / sizeof(*vec));
		if (!ok)
		{
			return -__LINE__;
		}
	}
	// find corresponding block
	Li_List ext = (Li_List)ptr_ext;
	Object objs = list_content(ext, Object_t);
	int ind = searchObj3_t(ext, _Ext_end_callback);
	if (ind < 0)
	{
		return -__LINE__;
	}
	CompileProtocol callback = (CompileProtocol)(objs[ind].data);
	if (callback != _compile_for_end && callback != _compile_loop_end && callback != _compile_while_end)
	{
		return -__LINE__;
	}
	_push_ins(ins, args, _continue, 0);
	return 0;
}
static int compile_break(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	if (*rx)
	{
		int vec[3];
		int *ok = fre_search2(_re_empty_pat, rx, len, vec, sizeof(vec) / sizeof(*vec));
		if (!ok)
		{
			return -__LINE__;
		}
	}
	// find corresponding block
	Li_List ext = (Li_List)ptr_ext;
	Object objs = list_content(ext, Object_t);
	int ind = searchObj3_t(ext, _Ext_end_callback);
	if (ind < 0)
	{
		return -__LINE__;
	}
	CompileProtocol callback = (CompileProtocol)(objs[ind].data);
	if (callback != _compile_for_end && callback != _compile_loop_end && callback != _compile_while_end)
	{
		return -__LINE__;
	}
	_push_ins(ins, args, _break, 0);
	return 0;
}
static int compile_end(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	if (len)
	{
		int vec[3];
		int *ok = fre_search2(_re_empty_pat, rx, len, vec, sizeof(vec) / sizeof(*vec));
		if (!ok)
		{
			return __ec;
		}
	}
	Li_List ext = (Li_List)ptr_ext;
	Object_t obj;
	if (_pop_ext(ext, _Ext_end_callback, &obj) < 0)
	{
		return __ec;
	}
	CompileProtocol callback = (CompileProtocol)(obj.data);
	return callback(rx, len, ins, args, ptr_ext);
}
#pragma endregion

static int _determine_assign(int *args)
{
	int n = *args++;
	char *ts = args;
	int m = n & (sizeof(Any) - 1) ? ((n & ~(sizeof(Any) - 1)) + sizeof(Any)) : n;
	int32_t *data = ts + m;
	for (int j = 0; j < n; j++)
	{
		int ope_type = (ts[j]) & 0x70;
		if (ope_type == RPECNumber) // instant number
		{
			(CVal *)data++;
			continue;
		}
		if (ope_type == RPECVar) // var
		{
			(HASH *)data++;
			continue;
		}
		if (ope_type == RPECOperator)
		{
			Any f = *data++;
			const str oper = _RPE_operator_to_str(f);
			if (*oper == '=' && oper[1] == 0)
			{
				return 1;
			}
			continue;
		}
		if (ope_type == RPECFun)
		{
			(HASH *)data++;
			continue;
		}
	}
	return 0;
}

/*
var a,b,c
var b=0
var c = 1+f(a,b), c
const a = r'123'
*/
static int compile_var(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	int _vec[12];
	while (len)
	{
		int *vec = fre_search2(_re_var_pat, rx, len, _vec, sizeof(_vec) / sizeof(*_vec));
		if (!vec)
		{
			return __ec;
		}
		str var_name = rx + vec[2];
		int var_name_len = _RE_GetGroupLen(vec, 1);
		rx += var_name_len;
		len -= var_name_len;
		if ((!_RE_GetGroupLen(vec, 2)))
		{
			if (len)
			{
				vec = fre_search2(_re_empty_pat, rx, len, _vec, sizeof(_vec) / sizeof(*_vec));
				if (!vec)
				{
					return __ec;
				}
			}
			// setu [name:str] [hs:HASH]
			HASH hs = getHashCode(var_name, var_name_len);
			int s_off = _push_arg_str(GetStrHeap(args), var_name, var_name_len);
			_push_ins(ins, args, _setu, 2, s_off, hs);
			return 0; // finish
		}
		if (rx[vec[4] - var_name_len] == ',')
		{
			HASH hs = getHashCode(var_name, var_name_len);
			int s_off = _push_arg_str(GetStrHeap(args), var_name, var_name_len);
			_push_ins(ins, args, _setu, 2, s_off, hs);
			rx++;
			len--;
			continue;
		}
		// cal
		// set var value
		int remains;
		len--;
		rx++;
		remains = RPE_compile_get_len(rx, len);
		if (remains < 0)
			return remains;
		int exp_len = len - remains;
		str exp = rx;
		rx += exp_len;
		len -= exp_len;
		if (remains)
			exp_len--;
		int ok = _compile_cal(exp, exp_len, ins, args);
		if (ok < 0)
			return ok;
		HASH hs = getHashCode(var_name, var_name_len);
		int s_off = _push_arg_str(GetStrHeap(args), var_name, var_name_len);
		_push_ins(ins, args, _set, 2, s_off, hs);
	}
	return 0;
}
/*
del a b c d
*/
static int compile_del(str rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
	return 0;
}

#pragma region variable

#pragma endregion

#pragma endregion

#pragma region single
// ------------------------------------------------------------
#define KW_if (13857u)
#define KW_elif (228924436u)
#define KW_else (228925745u)
#define KW_loop (244713352u)
#define KW_for (1765077u)
#define KW_while (921369849u)
#define KW_continue (2601041451u)
#define KW_break (3349216931u)
#define KW_end (1747771u)
#define KW_var (2037819u)

/*
.head 4 words
	total_size	= 0x007f3578
	code_offset	= 0x00000004
	args_offset	= 0x00000030
	txt_offset	= 0x0000abcd
.code:
	cal 1
	goto 2
	...
	end
.args:
	0xcdcdcdcd
	...
	0x12330000
.txt:
	"hello word"
	"this is a test formatting string"
*/
int32_t *pack_up(Li_List ins, Li_List args, Li_List ext, int *out_errcode)
{
	if (ext->count)
	{
		*out_errcode = -1;
		return NULL;
	}
	_push_ins(ins, args, _end, 0);
	Li_Ins _ins = list_content(ins, Li_Ins_t);
	Any *_args = list_content(args, Any);
	Li_List strheap = (Li_List)GetStrHeap(args);
	str _txt = list_content(strheap, char);
	int s_ins = ins->count * ins->type_size;
	int s_args = (args->count) * (args->type_size);
	int s_txt = strheap->count;
	int total_size = s_ins + s_args + s_ins + 4;
	int32_t *bs = (int32_t *)malloc(total_size);
	if (!bs)
	{
		*out_errcode = -2;
		return NULL;
	}
	int *p = bs;
	*p++ = total_size;
	*p++ = s_ins;
	*p++ = s_args;
	*p++ = s_txt;
	memcpy(p, _ins, s_ins);
	((int8_t *)p) += s_ins;
	memcpy(p, _args, s_args);
	((int8_t *)p) += s_args;
	memcpy(p, _txt, s_txt);
	return bs;
}

int compile_single(const char *rx, int len, Li_List ins, Li_List args, void *ptr_ext)
{
#define KWS S(if),S(elif),S(else),S(loop),S(for),S(while),S(continue),S(break),S(end),S(var)
#define S(kw) KW_##kw
	const HASH kw_hs[] = {KWS};
#undef S
#define S(kw) compile_##kw
	const CompileProtocol cc[] = {KWS};
#undef S
#undef KWS
	if (len < 0)
		len = strlen(rx);
	int *vec = fre_search(_re_first_token, rx, len);
	if (vec)
	{
		int lc_count = _RE_GetGroupLen(vec, 1);
		HASH hs = getHashCode(rx + vec[2], lc_count);
		for (int i = 0; i < sizeof(kw_hs) / sizeof(*kw_hs); i++)
		{
			if (kw_hs[i] != hs)
				continue;
			int rtc = (cc[i])(rx + vec[1], len - vec[1], ins, args, ptr_ext);
			return rtc;
		}
	}
	int idd = _compile_cal(rx, len, ins, args);
	if (idd < 0)
		return __ec;
	if (_determine_assign(list_content(args, Any) + idd - 1))
		return 0;
	// Li_List _block = RPE_compile(rx, len);
	// if (!_block)
	// {
	// 	return -1;
	// }
	// // CAL n, "epx"
	// _push_ins2(ins, args, _cal, 2);
	// _push_args2(args, (_block->count));
	// _push_args3(args, _block->content, _block->type_size * _block->count);
	// delete_list(_block);
	// ANS
	_push_ins(ins, args, _ans, 0);
	return 0;
}

int *compile_all(const str rx, int len)
{
	_compile_re_setup();
	RPE_init();
	Li_List ins = new_li_cap(Li_Ins_t, 32);
	Li_List args = _new_args_array();
	Li_List ext = new_li(Object_t);
	str s = rx;
	int er_code = 0;
	int state = 0;
	str start = rx;
	// ([\n;#']|r['"])
	// be cation about the string, the raw string, comments
	// for the '\n' and ';', just stop and compile current statement
	// for the string, go continue until after meet a EoS decimator
	int _vec[4 * 3];
	int *vec = NULL;
	while (len)
	{
		switch (state)
		{
		case 0:
			vec = fre_search2(_re_delimiter_general, s, len, _vec, sizeof(_vec) / sizeof(*vec));
			if (!vec)
				break; // end of file
			char t = s[vec[0]];
			if (t == '\n' || t == ';')
			{
				int one_len = s + vec[0] - start;
				if (!one_len)
				{
					start += vec[1];
					break;
				}
				printf("%.*s\n", one_len, start);
				int ok = compile_single(start, one_len, ins, args, ext);
				if (ok < 0)
					goto er;
				start = s + vec[1];
				break;
			}
			if (t == '#')
			{
				state = 1;
				int one_len = s + vec[0] - start;
				if (!one_len)
				{
					start += vec[1];
					break;
				}
				printf("%.*s\n", one_len, start);
				int ok = compile_single(start, one_len, ins, args, ext);
				if (ok < 0)
					goto er;
			}
			else // quote
			{
				int raw_str_len = 0;
				if (t == 'r')
				{
					int ok = str_translate_get_len_r(s + vec[0] + 1, &raw_str_len);
					if (ok < 0)
					{
						// the string is not closed
						goto er;
					}
					raw_str_len--;
				}
				else
				{
					int ok = str_translate_get_len(s + vec[0], &raw_str_len);
					raw_str_len--;
					if (ok < 0)
					{
						// the string is not closed
						goto er;
					}
				}
				// to skip the string, so that to scan from end of the string
				s += raw_str_len;
				len -= raw_str_len;
			}
			break;
		case 1: // in a comment
			vec = fre_search2(_re_delimiter_eos, s, len, _vec, sizeof(_vec) / sizeof(*vec));
			if (!vec)
				break;
			start = s + vec[1]; // en of the comment
			state = 0;
			break;
		default:
			break;
		}
		if (!vec)
			break;
		s += vec[1];
		len -= vec[1];
	}
	if (len) // left a string in the last line
	{
		if (state == 0)
		{
			int ok = compile_single(s, len, ins, args, ext);
			if (ok < 0)
				goto er;
		}
	}
	int *bs = pack_up(ins, args, ext, &er_code);
	delete_list(ins);
	delete_list(ext);
	_delete_args_array(args);
	return bs;
er:
	delete_list(ins);
	delete_list(ext);
	_delete_args_array(args);
	return NULL;
}

#undef KW_if
#undef KW_elif
#undef KW_else
#undef KW_loop
#undef KW_for
#undef KW_while
#undef KW_continue
#undef KW_break
#undef KW_end
#undef KW_var
/*
def HC(s:str):
  hash=0
  seed=131
  for c in s:
	hash = (((hash*seed)&0xffffffff)+ord(c))&0xffffffff
  return hash

# %%
cs = 'if,elif,else,loop,for,while,continue,break,end'.split(',')
for s in cs:
  hs = HC(s)
  print(f'#define KW_{s} ({hs}u)')

#%%

for s in cs:
  print(f'#undef KW_{s}')

#%%
for s in cs:
  hs = HC(s)
  print(f'S({s}),',end='')

*/
#pragma endregion

#pragma region test
#define testSuit 3
#if testSuit == 3
#include <stdio.h>
#include "li_vm.h"
int main(int n, char *argv[])
{
	_compile_re_setup();
	RPE_init();
	Li_List ins = new_li_cap(Li_Ins_t, 32);
	Li_List args = _new_args_array();
	Li_List ext = new_li(Object_t);
	Li_CB_t cb;
	cb.sp = (Li_SF)malloc(1024);
	int __sys_init_stack(Li_SF sf);
	__sys_init_stack(cb.sp);
	str cmds =
		"var a='this\\n is\\t\\v \\\"a word';"
		"var b,c;"
		"var d=1;"
		"var dx=1,s=r'as a\\n <s>';"
		"var e=1+2,f=12;"
		"\n;\n"
		"a;"
		"b=12;"
		"b;"
		"c=b;"
		"c;"
		"d+2+dx;"
		"a+s;"
		"e+f;";
	"\"abc->\\\"\"  'hello this is a help command\\n\\tass';"
	" 'hi'  +	r'hello this is a help command\\n\\tass';"
	" 1+1;"
	" for i=0:10 \n"
	" if i > 5 ;#jieesd \n"
	"	i	 ;	"
	" else	;	"
	"    if    i  <  3   ;  	"
	"-(i^2);"
	"'hello this is a help command\\n\\tass';"
	"r'hello''this is a help command\\n\\tass';"
	"else;"
	"1&&1;"
	"false&&true;"
	"end;"
	"end;"
	"end\n";
	str s = _strdup(cmds);
	Li_List ls = str_split_by_str(s, "\n;");
	int s_n = ls->count;
	str *ss = list_content(ls, str);

	// for (int i = 0; i < s_n; i++)
	// {
	// 	int ok = compile_single(ss[i], strlen(ss[i]), ins, args, (Any)ext);
	// 	if (ok < 0)
	// 	{
	// 		printf("compile failed at '%s'\a\nerror code=%d", ss[i], ok);
	// 		return ok;
	// 	}
	// }
	// _push_ins(ins, args, _end, 0);
	int *codes = compile_all(cmds, strlen(cmds));
	if (!codes)
	{
		printf("compile error \a\n");
	}
	printf("--------------------------------------\n");
	disassemble_from_bytes_block(codes);
	printf("--------------------------------------\n");

	int total_size = *codes;
	// pack_up
	int8_t *_p = codes + 4;
	cb.pc = _p; // +codes[1];
	_p += codes[1];
	int *args_heap = _p;
	_p += codes[2];
	cb.txt = _p;

	int _run_instruction_all(Li_CB cb, Any args_array[]);
	// cb.pc = list_content(ins, Li_Ins_t);
	// Li_List strheap = (Li_List)GetStrHeap(args);
	// cb.txt = list_content(strheap, char);
	// disassemble(ins, args);
	// printf("--------------------------------------\n");
	int rtc = _run_instruction_all(&cb, args_heap);
	// printf("--------------------------------------\n");
	// disassemble(ins, args);
	// return 0;
	// copy
	// int tsize = ins->count * ins->type_size;
	// cb.pc = (Li_Ins)malloc(tsize);
	// memcpy(cb.pc, ins->content, tsize);
	// tsize = args->count * args->type_size;
	// int *pargs = (int *)malloc(tsize);
	// memcpy(pargs, args->content, tsize);
	// tsize = strheap->count * strheap->type_size;
	// int *s2 = (int *)malloc(tsize);
	// memcpy(s2, strheap->content, tsize);
	// cb.txt = s2;
	// rtc = _run_instruction_all(&cb, (Any *)pargs);
	if (rtc < 0)
	{
		printf("%s\a\nunexpected error at line (%d)", cb.erMsg, rtc);
	}
	return rtc;
}
#endif
#pragma endregion
/*
var a='this\n is\t\v \"a word'
var b,c
var d=1
var dx=1,s=r'as a\n <s>'
var e=1+2,f=12
a
b=12
b
c=b
c
d+2+dx
a+s
e+f
------
ans=this
 is
 "a word
ans=12
ans=12
ans=4
ans=this
 is
 "a wordas a\n <s>
ans=15

*/